#include "svma.h"
#include "mmalign.h"
#include "assert.h"

#define CPU_BITWIDE_ALIGN(v) (((v) + MX_CPU_BITWIDE_MASK) & ~MX_CPU_BITWIDE_MASK)

/**
 * function [ mx_svma_mmuconfig ] implementation
 * @prototype: mx_usize_t mx_svma_mmuconfig (void);
 *      @param: void
 *      @return: granule size, it's both the MMU granule size and the block size of `mmalign` allocation
 * @desc: MMU configuration
 */
extern mx_usize_t mx_svma_mmuconfig (void);

/**
 * function [ mx_svma_sysmap ] implementation
 * @prototype: void mx_svma_sysmap (void);
 *      @param: void
 *      @return: void
 * @desc: system mmu map
 */
extern void mx_svma_sysmap (void);

/* variables from link script */
extern mx_void_pt __heap_start;
extern mx_void_pt __heap_end;
/* heap 4k page mangement */
static mx_mmalign_t mmalign_heap;
/* memory space descriptor for svma */
static mx_svma_tbm_t tbm;

/**
 * TODO: 最好能加一个参数，指定分配内存按照 n 对齐
 */
mx_void_pt mx_svma_tbm_alloc (mx_usize_t size)
{
    mx_void_pt ret = mx_null;
    size = CPU_BITWIDE_ALIGN(size);
    mx_addr_t newap = (mx_addr_t)tbm.apointer + size;
    if (newap < (mx_addr_t)tbm.end)
    {
        ret = tbm.apointer;
        tbm.apointer = (mx_void_pt)newap;
    }
    return ret;
}

void mx_svma_tbm_free (mx_usize_t size)
{
    // TODO: 实现 mx_svma_tbm_free 函数，暂定 mmalloc 实现
}

mx_void_pt mx_svma_heap_alloc ()
{
    mx_void_pt ret = mx_mmalign_alloc(&mmalign_heap);
    assert_true(ret != mx_null);
    return ret;
}

void mx_svma_heap_free (mx_void_pt addr)
{
    mx_mmalign_free(&mmalign_heap, addr);
}

/**
 * virtual memory system architecture initialization
 * @param start: start address of a valid memory space
 * @param size: the size of table memory
 * @return: void
 */
void mx_svma_init (mx_void_pt start, mx_usize_t size)
{
    assert_true(((mx_addr_t)start & MX_CPU_BITWIDE_MASK) == 0);
    assert_true(((mx_addr_t)size & MX_CPU_BITWIDE_MASK) == 0);

    /* table memory initialization */
    tbm.start = start;
    tbm.size = size;
    tbm.end = (mx_void_pt)((mx_usize_t)start + size);
    tbm.apointer = start;

    /* virtual memory configuration */
    mx_usize_t gz = mx_svma_mmuconfig();
    assert_true((gz & MX_CPU_BITWIDE_MASK) == 0);

    /* heap memory mangement */
    mx_usize_t bcount = ((mx_usize_t)&__heap_end - (mx_usize_t)&__heap_start) / gz;
    mx_void_pt iaddr = mx_svma_tbm_alloc(MX_MMALIGN_RASIZE(bcount));
    assert_true(iaddr != mx_null);
    mx_mmalign_init(&mmalign_heap, &__heap_start, iaddr, gz, bcount);

    /* system mmu map */
    mx_svma_sysmap();

    /* convert iaddr's pa in `mmalign_heap` to va */
    mmalign_heap.iaddr = MX_KERNEL_P2V(mmalign_heap.iaddr);
}
